﻿using System;
using System.Linq;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using Ah.Testudin.Engine.Actors;
using Ah.Testudin.Engine.Actors.Components;
using Ah.Testudin.Engine.Utilities;
using Ah.Testudin.Engine.Actors.Components.Animation;
using Ah.Testudin.Engine.Events;

namespace Ah.Testudin.Engine.AI
{
    public class PatrolBehavior : IBehavior
    {
        #region Constants

        private const float PATROL_SPEED_MULTIPLIER = .75f;

        private const short DOT_PRECISION = 3;

        #endregion

        #region Private Member Variables

        private Actor _actor;

        private int _waitTime;

        private int _waitTimer;

        private IEnumerable<Vector3> _patrolPoints;

        private short _currentPatrolPoint;

        private bool _hasFinishedRotating;

        private float _actorMoveSpeed;

        private float _slerpAmount;

        private bool _rotating;

        private IEventManager _eventManager;

        #endregion

        #region Constructors

        public PatrolBehavior(
            Actor actor,
            int waitTime,
            float actorMoveSpeed,
            IEnumerable<Vector3> patrolPoints,
            IEventManager eventManager)
        {
            if (actor == null)
            {
                throw new ArgumentNullException("actor");
            }

            if (waitTime < 0)
            {
                throw new ArgumentOutOfRangeException("waitTime");
            }

            _actor = actor;

            _waitTime = waitTime;

            _actorMoveSpeed = actorMoveSpeed;

            _eventManager = eventManager;

            _waitTimer = 0;

            _patrolPoints = patrolPoints;

            _currentPatrolPoint = 0;

            _hasFinishedRotating = false;

            _rotating = false;

            _slerpAmount = 0f;
        }

        #endregion

        #region IBehavior

        public void Update(GameTime gameTime)
        {
            if (_waitTimer > 0)
            {
                _waitTimer -= gameTime.ElapsedGameTime.Milliseconds;

                return;
            }

            TransformComponent transformComponent = _actor.GetComponent<TransformComponent>();

            Vector3 position = transformComponent.Transform.Translation;

            Vector3 currentPatrolPoint = _patrolPoints.ElementAt(_currentPatrolPoint);

            Vector3 difference = (currentPatrolPoint - position);

            if (_hasFinishedRotating == false)
            {
                Vector3 lookAtDirection = Vector3.Normalize(difference);
                
                Vector3 forward = Vector3.Normalize(transformComponent.Transform.Forward);

                float forwardDot = Vector3.Dot(
                    forward,
                    lookAtDirection);

                if (forwardDot != 1.0f)
                {
                    if (_rotating == false)
                    {
                        _rotating = true;

                        _actor.SetComponent<IAnimationComponent>(
                            new RotationAnimationComponent(
                                _actor,
                                lookAtDirection,
                                _eventManager,
                                3000f));
                    }
                }
                else
                {
                    _rotating = false;

                    _hasFinishedRotating = true;
                }
            }
            else
            {
                float distanceToPatrolPoint = difference.Length();

                if (distanceToPatrolPoint < .1f)
                {
                    UpdateToNextPatrolPoint();
                }
                else
                {
                    Vector3 velocityVector = Vector3.Normalize(difference);

                    float speed = ((float)(_actorMoveSpeed * PATROL_SPEED_MULTIPLIER)) * 
                                  ((float)((float)gameTime.ElapsedGameTime.Milliseconds / 1000f));

                    velocityVector *= speed;

                    if (velocityVector.Length() > distanceToPatrolPoint)
                    {
                        velocityVector = difference;
                    }

                    Vector3 newPosition = transformComponent.Transform.Translation + velocityVector;

                    PhysicsComponent physicsComponent = _actor.GetComponent<PhysicsComponent>();

                    physicsComponent.SetPosition(newPosition);
                }
            }
        }

        #endregion

        #region Private Methods

        private void UpdateToNextPatrolPoint()
        {
            _eventManager.QueueEvent(
                new MissileFiredEvent(
                    _actor.Id));

            _currentPatrolPoint = (_currentPatrolPoint == (_patrolPoints.Count() - 1))
                ? (short)0
                : (short)(_currentPatrolPoint + 1);

            _hasFinishedRotating = false;

            _waitTimer = _waitTime;

            _slerpAmount = 0f;
        }

        #endregion
    }
}
